package com.springBoot.springBootSysCore.modules.shiro.stateles;

import com.springBoot.springBootSysCore.common.StaticConfig;
import com.springBoot.springBootSysCore.common.utils.CacheUtils;
import com.springBoot.springBootSysCore.common.utils.encoding.HmacSHA256Utils;
import com.springBoot.springBootSysCore.modules.entity.system.SysUser;
import com.springBoot.springBootSysCore.modules.repository.system.SysUserRepository;
import com.springBoot.springBootSysCore.modules.services.SystemService;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * Created by timo on 2017/11/28.
 */
//@Component
public class StatelessRealm extends AuthorizingRealm {
    private Logger logger = LoggerFactory.getLogger(getClass());
    @Autowired
    private SysUserRepository sysUserRepository;

    public boolean supports(AuthenticationToken token) {
        //仅支持StatelessToken类型的Token
        return token instanceof StatelessToken;
    }
    public AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//        //根据用户名查找角色，请根据需求实现
//        String username = (String) principals.getPrimaryPrincipal();
//        SimpleAuthorizationInfo authorizationInfo =  new SimpleAuthorizationInfo();
//        authorizationInfo.addRole("admin");
//        return authorizationInfo;
        return null;
    }
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //			访问时使用该消息摘要进行传播，服务端然后对该消息摘要进行验证。如果只传递用户名+密码的消息摘要，一旦被别人捕获可能会重复使用该摘要进行认证。
// 			解决办法如：
//			1、每次客户端申请一个Token，然后使用该Token进行加密，而该Token是一次性的，即只能用一次；有点类似于OAuth2的Token机制，但是简单些；
//			2、客户端每次生成一个唯一的Token，然后使用该Token加密，这样服务器端记录下这些Token，如果之前用过就认为是非法请求。

//			另外关于可能会重复使用该摘要进行认证这个问题，除了楼主的两个方法外，
// 			还可以采用在生成消息摘要时加入当前时间，并在请求头中把这个时间一起传给服务器。
// 			服务器获得该时间后，采用相同算法算出该消息摘要，然后可以进一步判断该请求是否在有效时间，如果该请求超过一定的时间范围，则视为无效请求。
//			另外补充一点，在消息摘要中加入时间，其实是有效的抵制了“重放攻击”  ：）

//        http://127.0.0.1:8080/test/api/code/login?digest=dadadswdewq2ewdwqdwadsadasd&username=admin&a=1
        StatelessToken statelessToken = (StatelessToken) token;
        String username = statelessToken.getUsername();
        String key = getKey(username);//根据用户名获取密钥（和客户端的一样）
        //在服务器端生成客户端参数消息摘要
        String serverDigest = HmacSHA256Utils.digest(key, statelessToken.getParams());
        //然后进行客户端消息摘要和服务器端消息摘要的匹配
        return new SimpleAuthenticationInfo(username,serverDigest,getName());
    }

    private String getKey(String username) {//得到密钥
//        CacheUtils.put(StaticConfig.PARAM_DIGEST+username,clientDigest);
//        Object clientDigest = CacheUtils.get(StaticConfig.PARAM_DIGEST+username);
//        System.out.println("=============消息摘要："+clientDigest+"==================");
        SysUser u = sysUserRepository.findByAccount(username);
        if(u!=null){
            return u.getClientDigest();
//            return clientDigest.toString();
        }else{
            return null;
        }
    }
}